<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>slider</title>
    <style>
        .slide-container {
            position: relative;
            width: 400px;
            height: 200px;
            margin: 20px auto;
            overflow: hidden;
            cursor: move;
        }
        .slide-container .card {
            position: absolute;
            width: 100px;
            height: 180px;
            top: 10px;
            left: 150px;
            text-align: center;
            font: 500 72px/180px Arial;
            border: 1px solid;
            box-sizing: border-box;
        }
        .slide-container .card.prev {
            margin-left: -252px;
        }
        .slide-container .card.next {
            margin-left: 252px;
        }
        .preview-container {
            overflow: hidden;
            width: 240px;
            margin: 20px auto;
        }
        .preview-container .card {
            float: left;
            width: 48px;
            margin: 0 5px;
            height: 90px;
            text-align: center;
            font: 100 32px/90px Arial;
            border: 1px solid;
            cursor: pointer;
        }
        .preview-container .card.focus {
            border-color: red;
            color: red;
        }
    </style>
</head>
<body>

    <div class="slide-container">
        <div class="card prev"></div>
        <div class="card">01</div>
        <div class="card next"></div>
    </div>
    <div class="preview-container">
        <div class="card">春</div>
        <div class="card">夏</div>
        <div class="card">秋</div>
        <div class="card">冬</div>
    </div>

    <div class="preview-container">
        <button class="prev">上一个</button>
        <button class="next">下一个</button>
    </div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-ease/1.0.3/d3-ease.js"></script>
<script>
(function () {
    var imgs = "01,02,03,04,05,06,07,08,09,10,11".split(",")
    var img_len = imgs.length
    var preview_pool = [[0, 1], [2, 3, 4], [5, 6, 7, 8], [9, 10]]
    var preview_map = preview_pool.reduce((m, arr, i) => {
        arr.map(t => m[t] = i)
        return m
    }, {})
    var cards = document.querySelectorAll('.slide-container > .card')
    var card_prev = cards[0]
    var card_curr = cards[1]
    var card_next = cards[2]

    var previews = document.querySelectorAll('.preview-container > .card')
    const exec = function (i) {
        i = (i + img_len) % img_len
        var n = Math.floor(i + .5)
        var curr = n % img_len
        var prev = (n - 1 + img_len) % img_len
        var next = (n + 1) % img_len

        card_prev.innerHTML = imgs[prev]
        card_curr.innerHTML = imgs[curr]
        card_next.innerHTML = imgs[next]

        for (let i = 0; i < previews.length; i++) {
            const ele = previews[i];
            if (i === preview_map[curr]) {
                ele.classList.add('focus')
            } else {
                ele.classList.remove('focus')
            }
        }
        const transform = `transform: translateX(${(n - i) * 250}px)`
        card_prev.style.cssText = transform
        card_curr.style.cssText = transform
        card_next.style.cssText = transform
    }

    let index = 0
    exec(0)

    const btn_prev = document.querySelector('.preview-container .prev')
    const btn_next = document.querySelector('.preview-container .next')

    const prev_next = (dir = 1) => {
        return function () {
            if (btn_prev.getAttribute('disabled')) {
                return
            }
            let t = 0
            btn_prev.setAttribute('disabled', true)
            btn_next.setAttribute('disabled', true)
            function loop () {
                /** 
                 * 可选动画函数包括
                 * 
                 * easeLinear, easeQuad, easeQuadIn, easeQuadOut, easeQuadInOut,
                 * easeCubic, easeCubicIn, easeCubicOut, easeCubicInOut, easePoly,
                 * easePolyIn, easePolyOut, easePolyInOut, easeSin, easeSinIn,
                 * easeSinOut, easeSinInOut, easeExp, easeExpIn, easeExpOut,
                 * easeExpInOut, easeCircle, easeCircleIn, easeCircleOut, easeCircleInOut,
                 * easeBounce, easeBounceIn, easeBounceOut, easeBounceInOut, easeBack, easeBackIn,
                 * easeBackOut, easeBackInOut, easeElastic, easeElasticIn, easeElasticOut, easeElasticInOut
                **/
                const d = d3.easeElastic(t)
                exec(index + d * dir)
                if (t < 1) {
                    t += 1 / 60
                    requestAnimationFrame(loop)
                } else {
                    btn_prev.removeAttribute('disabled')
                    btn_next.removeAttribute('disabled')
                    index += dir
                }
            }
            loop()
        }
    }

    btn_prev.addEventListener('click', prev_next(-1))
    btn_next.addEventListener('click', prev_next(1))
    previews.forEach(function (el, i) {
    	el.addEventListener('click', function () {
            prev_next(preview_pool[i][0] - index)()
        })
    })

    const container = document.querySelector('.slide-container')
    let touchmove = null
    const onTouchStart = function (e) {
        let x = e.clientX || e.touches[0].clientX
        touchmove = { x }
    }
    const onTouchMove = function (e) {
        if (touchmove) {
            let x = e.clientX || e.touches[0].clientX
            exec(index + (touchmove.x - x) / 250)
            touchmove.x1 = x
        }
    }
    const onTouchEnd = function () {
        if (touchmove) {
            const _index = index + (touchmove.x - touchmove.x1) / 250
            const t_index = Math.floor(_index + .5)
            const dir = t_index - _index
            let t = 0
            function loop () {
                const d = d3.easeElasticOut(t)
                exec(_index + d * dir)
                if (t < 1) {
                    t += 1 / 30
                    requestAnimationFrame(loop)
                } else {
                    index = t_index
                }
            }
            if (touchmove.x1) {
                loop()
            }
            touchmove = null
        }
    }
    container.addEventListener('mousedown', onTouchStart)
    container.addEventListener('mousemove', onTouchMove)
    container.addEventListener('mouseup', onTouchEnd)
    container.addEventListener('mouseleave', onTouchEnd)

    container.addEventListener('touchstart', onTouchStart)
    container.addEventListener('touchmove', onTouchMove)
    container.addEventListener('touchend', onTouchEnd)
})()
</script>
</body>
</html>